package com.lhq.tag_tales.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.lhq.tag_tales.common.ErrorCodeEnum;
import com.lhq.tag_tales.exception.BusinessException;
import com.lhq.tag_tales.mapper.UserMapper;
import com.lhq.tag_tales.model.domain.User;
import com.lhq.tag_tales.service.UserService;
import com.lhq.tag_tales.utils.PasswordUtils;
import com.lhq.tag_tales.utils.UserUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.lhq.tag_tales.constant.RedisConstant.RECOMMEND_USER_PREFIX;
import static com.lhq.tag_tales.constant.RedisConstant.RECOMMEND_USER_TO;
import static com.lhq.tag_tales.constant.UserConstant.*;


/**
 * @author L_curiosity
 * @description 针对表【user(用户表)】的数据库操作Service实现
 * @createDate 2024-10-22 20:22:52
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    @Resource
    private UserMapper userMapper;
    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword, String planetCode) {
        //校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "参数为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "用户账号过短");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码过短");
        }
        if (planetCode.length() > 5) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "星球码过长");
        }
        //账号不能包含特殊字符
        if (!userAccount.matches(VALID_ACCOUNT_REGEX)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "账号不能包含特殊字符");
        }
        // 密码和校验密码相同
        if (!StringUtils.equals(userPassword, checkPassword)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码不一致");
        }
        // 判断账户是否重复 【尽量将查询数据库的校验放到后面】
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_account", userAccount);
        long count = userMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCodeEnum.DUPLICATE_ERROR, "账号已经注册");
        }
        // 判断星球编码是否重复 【尽量将查询数据库的校验放到后面】
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("planet_code", planetCode);
        count = userMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "星球编码已经存在");
        }

        // 密码加密
        User user = new User();
        user.setUserPassword(PasswordUtils.encryptPassword(userPassword));
        user.setUserName(userAccount);
        user.setUserAccount(userAccount);
        user.setPlanetCode(planetCode);
        boolean save = save(user);
        if (!save) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "注册失败");
        }
        return user.getId();
    }

    @Override
    public User userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        //校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "账号或密码为空");
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "账号名称过短");
        }
        if (userPassword.length() < 8) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "密码过短");
        }
        //账号不能包含特殊字符
        if (!userAccount.matches(VALID_ACCOUNT_REGEX)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "账号不能包含特殊字符");
        }
        //判断用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        String encryptPassword = DigestUtils.md5DigestAsHex((userPassword + SALT).getBytes());
        queryWrapper.eq("user_account", userAccount)
                .eq("user_password", encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            log.warn("user login failed, userAccount can not match password");
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "用户不存在");
        }
        // 返回脱敏信息
        User safetyUser = getSafetyUser(user);
        //记录用户登录态
        request.getSession().setAttribute(USER_LOGIN_STATE, safetyUser);
        return safetyUser;
    }

    @Override
    public List<User> searchUsers(String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(username)) {
            queryWrapper.like("user_name", username);
        }
        //如果账号为空，则查询所有用户
        return userMapper.selectList(queryWrapper);
    }

    /**
     * 脱敏用户信息
     *
     * @param user 原始用户信息
     * @return 脱敏用户信息
     */
    @Override
    public User getSafetyUser(User user) {
        //如果用户为空，直接返回
        if (user == null) {
            throw new BusinessException(ErrorCodeEnum.NULL_ERROR, "用户不存在");
        }
        User safetyUser = new User();
        safetyUser.setId(user.getId());
        safetyUser.setUserName(user.getUserName());
        safetyUser.setUserAccount(user.getUserAccount());
        safetyUser.setAvatarUrl(user.getAvatarUrl());
        safetyUser.setGender(user.getGender());
        safetyUser.setPhone(user.getPhone());
        safetyUser.setEmail(user.getEmail());
        safetyUser.setIsValid(user.getIsValid());
        safetyUser.setUpdateTime(user.getUpdateTime());
        safetyUser.setRole(user.getRole());
        safetyUser.setPlanetCode(user.getPlanetCode());
        safetyUser.setTags(user.getTags());
        return safetyUser;
    }

    /**
     * 用户注销
     *
     * @param request http请求
     * @return
     */
    @Override
    public int userLogout(HttpServletRequest request) {
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return 0;
    }

    @Override
    public int updateUser(User user,User currentUser) {
        Long userId = user.getId();
        if (userId == null){
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "参数错误");
        }
        //如果当前用户没有传任何要更细的值，则直接返回
        if (userIsNull(user)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "参数为空");
        }
        //是当前用户，只能修改自己的信息
        if (!Objects.equals(userId, currentUser.getId())|| !isAdmin(currentUser)){
            throw new BusinessException(ErrorCodeEnum.NO_AUTH, "无权限");
        }
        //查询数据库
        User oldUser = userMapper.selectById(userId);
        //如果数据库中不存在该用户
        if (oldUser == null){
            throw new BusinessException(ErrorCodeEnum.NULL_ERROR, "用户不存在");
        }
        //更新用户信息
        return userMapper.updateById(user);
    }

    @Override
    public boolean deleteUserById(long id) {
        //查询用户
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new BusinessException(ErrorCodeEnum.NULL_ERROR, "用户不存在");
        }
        long roleId = user.getRole();
        User currentUser = UserUtils.getCurrentUser();
        if (currentUser == null) {
            throw new BusinessException(ErrorCodeEnum.NO_LOGIN, "登录状态失效");
        }
        if (currentUser.getRole() <= roleId) {
            throw new BusinessException(ErrorCodeEnum.NO_AUTH, "无权限");
        }
        //执行删除操作
        return removeById(id);
    }

    @Override
    public List<User> searchUsersByTags(List<String> tagNameList) {
        //1. 判断参数是否为空或空字符
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "标签列表为空");
        }
        //2. 构建查询条件【只要存在其中一个标签就匹配】
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        for (String tagName : tagNameList) {
            queryWrapper.like("tags", tagName);
        }
        //3. 判断查询的用户列表是否为空
        List<User> userList = userMapper.selectList(queryWrapper);
        return Optional.ofNullable(userList)
                .filter(list -> !list.isEmpty())  // 判断列表是否为空
                .orElseThrow(() -> new BusinessException(ErrorCodeEnum.NULL_ERROR, "没有与标签相匹配的用户"))
                .stream()
                .map(this::getSafetyUser)
                .collect(Collectors.toList());
    }

    @Override
    public int updateUserAvatar(User user) {
        //判断用户是否拥有权限
        Long role = user.getRole();
        if (role != 1){
            throw new BusinessException(ErrorCodeEnum.NO_AUTH,"权限不足");
        }
        //更新用户数据
        return userMapper.updateById(user);
    }


    @Override
    public boolean isAdmin(){
        User currentUser = UserUtils.getCurrentUser();
        return currentUser != null && currentUser.getRole() == 1;
    }

    /**
     * 判断是否为管理员
     * @param loginUser 当前登录的用户
     * @return true是 false不是
     */
    @Override
    public boolean isAdmin(User loginUser) {
        return loginUser != null && loginUser.getRole() == 1;
    }

    @Deprecated
    private List<User> searchByCache(List<String> tagNameList) {
        //1. 判断参数是否为空或空字符
        if (CollectionUtils.isEmpty(tagNameList)) {
            throw new BusinessException(ErrorCodeEnum.PARAMS_ERROR, "标签列表为空");
        }
        //2. 查询所有的用户
        List<User> userList = userMapper.selectList(null);
        if (CollectionUtils.isEmpty(userList)) {
            throw new BusinessException(ErrorCodeEnum.NULL_ERROR, "用户不存在");
        }
        Gson gson = new Gson();
        //在内存中判断是否存在包含某标签的用户
        return userList.parallelStream().filter(user -> {
            String tags = user.getTags();
            //如果标签为空，直接返回
            if (StringUtils.isBlank(tags)) {
                return false;
            }
            Set<String> tagList = gson.fromJson(tags, new TypeToken<Set<String>>() {
            }.getType());
            for (String tag : tagNameList) {
                if (!tagList.contains(tag)) {
                    return false;
                }
            }
            return true;
        }).map(this::getSafetyUser).collect(Collectors.toList());
    }

    /**
     * 当用户所有属性都为null时，返回true
     * @param user
     * @return
     */
    @Override
    public boolean userIsNull(User user){
        return ObjectUtils.allNull(
                user.getUserName(),
                user.getUserAccount(),
                user.getAvatarUrl(),
                user.getGender(),
                user.getUserPassword(),
                user.getPhone(),
                user.getEmail(),
                user.getIsValid(),
                user.getIsDeleted(),
                user.getCreateTime(),
                user.getUpdateTime(),
                user.getRole(),
                user.getPlanetCode(),
                user.getTags()
        );
    }

    @Override
    public Page<User> recommendUser(long pageSize, long pageNum) {
        User loginUser = UserUtils.getCurrentUser();
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        String key = RECOMMEND_USER_PREFIX + loginUser.getId();
        //判断缓存中是否存在推荐数据
        Page<User> userPage = (Page<User>)ops.get(key);
        if (userPage != null) {
            return userPage;
        }
        //current：当前页在整体结果集中的起始位置
        userPage = page(new Page<>((pageNum - 1) * pageSize, pageSize));
        //数据脱敏
        userPage.setRecords(userPage.getRecords().stream()
                .map(this::getSafetyUser)
                .collect(Collectors.toList()));
        try {
            ops.set(key, userPage, RECOMMEND_USER_TO, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("redis操作异常", e);
        }
        return userPage;
    }
}




